using System;
using System.Globalization;
using System.Runtime.Remoting.Messaging;
using System.Security.Permissions;

namespace MSTestExtensions
{
    /// <remarks />
    public class TestTimerAspect : TestAspect<TestTimerAttribute>, IMessageSink, ITestAspect
    {
        #region Fields

        /// <remarks />
        private IMessageSink _nextSink;

        #endregion // Fields

        #region IMessageSink Members

        /// <remarks />
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public IMessage SyncProcessMessage(IMessage msg)
        {
            if (msg == null)
                throw new ArgumentNullException("msg");
            DateTime timeBeforeTest = DateTime.Now;
            IMessage returnMessage = _nextSink.SyncProcessMessage(msg);
            DateTime timeAfterTest = DateTime.Now;
            TestTimerAttribute testTimerAttribute = GetAttribute(msg);
            if (testTimerAttribute != null)
            {
                TimeSpan targetTestLength = testTimerAttribute.TestLength;
                TimeSpan actualTestLength = timeAfterTest - timeBeforeTest;
                if (actualTestLength > targetTestLength)
                {
                    string exceptionMessage = string.Format(CultureInfo.CurrentCulture, "The test exceeded the TestTimer constraint. Target test length '{0}', actual test length '{1}'.", targetTestLength, actualTestLength);
                    throw new MSTestExtensionsException(exceptionMessage);
                }
            }
            return returnMessage;
        }

        /// <remarks />
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            throw new InvalidOperationException();
        }

        /// <remarks />
        public IMessageSink NextSink
        {
            [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
            get { return _nextSink; }
        }

        #endregion // IMessageSink Members

        #region ITestAspect

        /// <remarks />
        public void AddMessageSink(IMessageSink messageSink)
        {
            _nextSink = messageSink;
        }

        #endregion // ITestAspect
    }
}
